﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.proxy;
using snat.proxy.items;
using snat.proxy.sqlapi;
using System.Threading.Tasks;
namespace snat.model
{
    public class NetworkModel
    {
        /* Structure Network Model Properties */
        private int nodeCount;
        public int NodeCount {
            get {
                return nodeCount;
            }
        }
        private int edgeCount;
        public int EdgeCount {
            get {
                return edgeCount;
            }
        }

        /*** MOVE THIS TO STRUCTURE ALGORITHMS PACKAGE ***/
        private static NetworkModel instance;
        public static NetworkModel Instance {
            get {
                if (instance == null) {
                    instance = new NetworkModel();
                }
                return instance;
            }
        }

        private Dictionary<int, BaseNode> nodes;
        public Dictionary<int, BaseNode> Nodes {
            get {
                return nodes;
            }
        }
        private List<Edge> edges;
        public List<Edge> Edges {
            get {
                return edges;
            }
        }
        //ListProxy<Message> allMessages;
        private DateTime startTime;
        public DateTime StartTime {
            get {
                return startTime;
            }
        }
        private DateTime endTime;
        public DateTime EndTime {
            get {
                return endTime;
            }
        }
        private DateTime currentTime;
        public DateTime CurrentTime {
            get {
                return currentTime;
            }
        }

        private IEnumerator<Message> messageIterator;

        private NetworkModel() {
            nodes = new Dictionary<int, BaseNode>();
            edges = new List<Edge>();
        }

        private bool paused = true;
        public bool Paused {
            get {
                return paused;
            }
        }

        public void Play() {
            paused = false;
            while (!Paused) {
                Step(1);
            }
        }

        public void Init() {
            DataProxy.Instance.connect("siroberts.co.uk", 3306, "sirobert_snat", "sirobert_snag", "mOOch1ng");
            messageIterator = MessageQuery.All().GetEnumerator();
            messageIterator.MoveNext();
            startTime = messageIterator.Current.TimeStamp;
            currentTime = messageIterator.Current.TimeStamp;
            endTime = MessageQuery.LastMessage().TimeStamp;
            Console.WriteLine("Initializing the nodes...");
            foreach (Entity e in EntityQuery.All()) {
                nodes.Add(e.ID, new BaseNode(e.ID, e.Name));
            }
            nodeCount = nodes.Count;
            Console.WriteLine("Initializing the edges...");
            foreach (Connection c in ConnectionQuery.Init()) {
                edges.Add(new Edge(c, DateTime.MinValue));
            }
            edgeCount = edges.Count;
        }

        public void Pause() {
            paused = true;
        }

        void NetworkStructure(out Edge edges, out List<BaseNode> nodes) {
            throw new NotImplementedException("This method needed if saving the graph state is ever necessary");
        }

        public void triggerSend(BaseNode sender, Message m, List<BaseNode> recipients) {
            if (sender != null && sender.onSend != null) {
                sender.onSend(m, recipients);
            }
            else Console.WriteLine("Send not registered");
            Parallel.ForEach(recipients, recipient => {
                if (recipient != null && recipient.onRecieve != null) {
                    recipient.onRecieve(m, sender);
                }
                else Console.WriteLine("Recieve not registered");
            });
        }

        public void Step(int steps) {
            while (steps-- > 0) {
                Message current = messageIterator.Current;

                currentTime = current.TimeStamp;
                BaseNode from = nodes[current.Sender.ID];
                //trigger event for from.send
                List<BaseNode> to = new List<BaseNode>();
                foreach (Entity e in current.Recipients) {
                    BaseNode recipient = nodes[e.ID]; //don't bother newing it
                    if (recipient != null) to.Add(recipient);
                }
                triggerSend(from, current, to);

                Console.WriteLine(current.ToString());

                if (!messageIterator.MoveNext()) {
                    paused = true;
                    Console.WriteLine("FIN");
                    return;
                    //TODO tell people that the show's over
                }
            }
        }

        public int Step(DateTime time) {
            int i = 0;
            while (currentTime < time) {
                Step(1);
                i++;
            }
            return i;
        }

        internal void AddEdge(Edge newEdge) {
            edges.Add(newEdge);
            edgeCount++;
        }
    }
}
